unit uDriver.local;

interface

uses uDebug.common,
     uDefs.local,
     SysUtils;

type
  //================================ DRIVER
  TPitStop = class( TObject )
  public
    OnLap     : integer;
    Tyres     : integer;
    Fuel      : real;
    Completed : boolean;

    constructor Create();

    procedure SetPitStop(nOnLap,nTyres:integer;nFuel:real);
  end;

  TPitStops = array of TPitStop;

  TDriverItem = class( TObject )
  public
    IsBot        : boolean;
    BOT_AutoFuel : boolean;
    BOT_AutoTyre : boolean;

    //Imported Values
    DriverID     : integer;
    Name         : string;
    Nation       : string;
    TeamID       : integer;
    SkillSpeed   : integer;
    SkillFocus   : integer;
    Points       : integer;
    DriverSmooth : integer;

    //Race Data
    GridSlot              : integer;
    LapTime               : real;
    LastLapTime           : real;
    LapTimeDiff           : real;// From LastLapTime
    FastestLap            : TFastestLap;
    LeaderLapTimeDiff     : real;
    TimeToNextDriver      : real;
    TimeToLeader          : real;
    TotalTime             : real;
    TrackPosition         : integer;
    PreviousTrackPosition : integer;

    //Race Data...
    Fuel           : real;    // Fuel in litres
    FuelLaps       : integer; // Approx laps of fuel in car
    FuelStints     : integer; // Number of Stints.
    TotalStops     : integer; // Current stops made.
    FuelDiff       : integer; // Will be used to give drivers different strategies.
    FuelToFill     : integer; // A way for the player to pit a driver for fuel
    TyresToFit     : integer; // A way for the player to pit a driver for tyres.
    TyreHealth     : real;    // Effectiveness of the tyre.
    Tyres          : integer; // Which tyre driver is using

    //Race Data...
    Status       : TRaceStatus;
    Velocity     : real; //Measured in m/s
    Acceleration : real; //Measured in m/s
    Drag         : real; //Slipstream bonus and following penalty

    PitStops : TPitStops;

    constructor Create;
    destructor Destroy; override;
    procedure NewSession();
    function ChangeTyres(nTyresToFit:integer):real; //This function replaces the tyres with a new set, and adds time to the over all lap.
    procedure DegradeTyre(nTrackDistance:real;nTyreWear:integer);
    function AddFuel(Quantity,nFuelPerLap:real):real; //Measured in Litres
    procedure RemoveFuel(nFuelPerLap:real); //Measured in Litres
    function Fuel2Laps(nFuelPerLap:real):integer;
    function NeededFuel(nLaps,nFuelDiff:integer;nFuelPerLap:real):real;

    function PitStop(Lap,TotalLaps:integer;nFuelPerLap:real;IsRacing:boolean):real;

    function GetDrag():real;

  end;
  TDriverList = class( TObject )
  public
    Items : array of TDriverItem;
    Count : integer;

    constructor Create;
    destructor Destroy; override;

    procedure Add(nDriverID:integer;nName,nNation:string;nTeamID,nSkillSpeed,nSkillFocus,nDriverSmooth:integer);
    procedure NewSession(nDriverID:integer);

    function FindByName(nDriverName:string):TDriverItem;

    procedure Clear;
  end;

implementation

uses Math;

  //================================ PITSTOP

constructor TPitStop.Create();
begin
  OnLap     := -1;
  Tyres     := -1;
  Fuel      := -1;
  Completed := FALSE;
end;

procedure TPitStop.SetPitStop(nOnLap,nTyres:integer;nFuel:real);
begin
  OnLap     := nOnLap;
  Tyres     := nTyres;
  Fuel      := nFuel;
  Completed := FALSE;
end;

  //================================ DRIVERS
constructor TDriverItem.Create;
begin
  inherited Create;

  NewSession();
  Points := 0;
  IsBot  := TRUE;
  BOT_AutoFuel := TRUE;
  BOT_AutoTyre := FALSE;
end;
destructor TDriverItem.Destroy;
begin
  FreeAndNil( FastestLap );

  inherited Destroy;
end;
procedure TDriverItem.NewSession();
begin
  LapTime               := -1;
  LastLapTime           := -1;
  LapTimeDiff           := -1; // From LastLapTime

  FreeAndNil( FastestLap );

  FastestLap := TFastestlap.Create();
  FastestLap.SetTime( 0, -1, -1 );

  LeaderLapTimeDiff     := -1;
  TimeToNextDriver      := -1;
  TimeToLeader          := -1;
  TotalTime             := -1;
  TrackPosition         := -1;
  PreviousTrackPosition := -1;

  Fuel                  := 0; // Total Laps of Fuel
  FuelLaps              := 0; // Approx laps of fuel in car
  FuelStints            := 0; // Total number of stops
  TotalStops            := 0; // Stops made.
  FuelDiff              := 0; // Random number for +- of laps of fuel, for longer/shorter stints;
  FuelToFill            := -1;
  TyresToFit            := -1;
  TyreHealth            := 100;    // Effectiveness of the tyre.
  Tyres                 := -1;

  Status                := [STATUS_PIT]; // They start in the pits.
  Velocity              := 0; //Constant max Speed
  Acceleration          := 0; //This isnt use yet, but once the tracks are finished it will be...
  Drag                  := 0; //Slipstream/dirty air
end;

function TDriverItem.ChangeTyres(nTyresToFit:integer):real; //This function replaces the tyres with a new set, and adds time to the over all lap.
begin
  TyreHealth := 100;
  Tyres      := nTyresToFit;

  result := (random(4000)+2000)/1000; // between 2 and 6 Seconds stop.
end;
procedure TDriverItem.DegradeTyre(nTrackDistance:real;nTyreWear:integer);
var WearRate : real;
begin
  if (STATUS_BOOST    in Status) then
    WearRate := 0
  else
    WearRate := (((TYRE_WEAR_KM * nTrackDistance) * (DriverSmooth / 100) ) + (Random(1000)/10000) ); // Removes a percentage of the tyre effectivness per lap.

  WearRate := WearRate * (nTyreWear*0.01);

  TyreHealth := TyreHealth - WearRate;

  if (STATUS_LOCKUP   in Status) then
    TyreHealth := TyreHealth - (Random(1500)/1000);
end;
function TDriverItem.AddFuel(Quantity,nFuelPerLap:real):real; //Measured in Litres, returns pumping time. This can add fuel without adding time.
begin
  result := 0;

  if (Quantity <= 0) then exit;

//=====Add Fuel
  Fuel := Fuel + Quantity;

//=====Calc fueling time
  result := (random(1500)/1000) + (Quantity/FUEL_PUMP); // delay to do a pit stop

//=====Recalcs the projected laps with current fuel
  FuelLaps := Fuel2Laps(nFuelPerLap); // Recalc the predicted laps with fuel in the tank.
end;
procedure TDriverItem.RemoveFuel(nFuelPerLap:real);
begin
  Fuel := Fuel - nFuelPerLap;

  FuelLaps := Fuel2Laps(nFuelPerLap); // Recalc remaining fuel using current fuel.
end;
function TDriverItem.Fuel2Laps(nFuelPerLap:real):integer;
begin
  result := floor(Fuel / nFuelPerLap); // returns number of laps of fuel the car is carrying
end;
function TDriverItem.NeededFuel(nLaps,nFuelDiff:integer;nFuelPerLap:real):real;
begin
  result := nLaps * nFuelPerLap;
end;

function TDriverItem.PitStop(Lap,TotalLaps:integer;nFuelPerLap:real;IsRacing:boolean):real;
var FuelTime, TyreTime : real;
begin
//  DevConsole.Msg(ES_DEBUG, '[TDriverItem.PitStop()]');

  Result   := 0;
  FuelTime := 0;
  TyreTime := 0;

  if (FuelToFill > 0) then
    Status := Status + [STATUS_PIT] + [STATUS_FUEL] + [STATUS_TYRES];

  if (TyresToFit >= 0) then
    Status := Status + [STATUS_PIT] + [STATUS_TYRES];

  if (STATUS_PIT in Status) then
  begin
    DevConsole.Msg(ES_DEBUG, '[PITSTOP] '+name+' pits on lap '+IntToStr(Lap));

    //======================Refueling - needs work as pitstops still not
    if (STATUS_FUEL in Status) then
    begin
      if (FueltoFill > 0) then
      begin
        DevConsole.Msg(ES_DEBUG, '-- Fuel: '+FormatFloat('0.00',FuelToFill)+', Laps Added: '+FormatFloat('0.00', FuelToFill/nFuelPerLap) );
        FuelTime := AddFuel( NeededFuel( FuelToFill, 0, nFuelPerLap ), nFuelPerLap );
      end;

      FuelToFill := -1;
    end;
    //======================Tyres
    if (STATUS_TYRES in Status) then
    begin
      if (TyresToFit < 0) then // Default setting for tyres is the current set.
        TyresToFit := Tyres;

      DevConsole.Msg(ES_DEBUG, '-- Tyres: '+IntToStr(TyresToFit));

      TyreTime := ChangeTyres(TyresToFit);

      TyresToFit := -1;
    end;
    //======================Checker for Tyre and Fuel Times
    if (FuelTime > TyreTime) then
      result := result + FuelTime
    else
      result := result + TyreTime;
    //======================Stops
    if (IsRacing) then
      Inc( TotalStops );
  end;
//  DevConsole.Msg(ES_DEBUG, '#'+Name+' has '+FormatFloat('0.00', Fuel)+' Litres of fuel.');
end;
function TDriverItem.GetDrag():real;
begin
//  DevConsole.Msg(ES_DEBUG, '[TDriverItem.GetDrag()]');

  result := 0;

  if (STATUS_DIRTYAIR   in Status) then
    result := (0.3 -(TimeToNextDriver * 0.095))* (SkillFocus/100) // Dirty air.
  else
  if (STATUS_SLIPSTREAM in Status) then
    result := -0.15*(SkillFocus/100) //Slipstream
  else
  if (STATUS_PIGEON     in Status) then
    result := -0.1*(SkillFocus/100);//Catch the pigeon (aka lead driver)
end;

  //--------------------------------
constructor TDriverList.Create;
begin
  inherited Create;

  Count := 0;
  SetLength( Items, Count );
end;
destructor TDriverList.Destroy;
begin
  Count := 0;
  SetLength( Items, Count );

  inherited Destroy;
end;
procedure TDriverList.Add(nDriverID:integer;nName,nNation:string;nTeamID,nSkillSpeed,nSkillFocus,nDriverSmooth:integer);
begin
  DevConsole.msg( ES_DEBUG, 'Adding Driver '+nName);

  Inc( Count );
  SetLength(Items, Count);

  Items[ Count-1 ] := TDriverItem.Create();

  with Items[ Count-1 ] do
  begin
    DriverID     := nDriverID;
    Name         := nName;
    Nation       := nNation;
    TeamID       := nTeamID;
    SkillSpeed   := nSkillSpeed;
    SkillFocus   := nSkillFocus;
    DriverSmooth := nDriverSmooth;

    TyreHealth  := 100;

//    Items[ Count-1 ].Clear();
  end;
end;
procedure TDriverList.NewSession(nDriverID:integer);
begin
  with Items[ nDriverId ] do
  begin
  end;
end;
function TDriverList.FindByName(nDriverName:string):TDriverItem;
var D : Integer;
begin
  result := nil;

  for D := 0 to High( Items ) do
  begin
    if (Items[D].Name = nDriverName) then
      result := Items[D];
  end;
end;
procedure TDriverList.Clear;
begin
  Count := 0;
  SetLength( Items, Count );
end;

end.
